/*
 * Copyright 2006-2007 S W McIlroy
 * Licensed  under the  Apache License,  Version 2.0  (the "License");
 * you may not use  this file  except in  compliance with the License.
 * You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed  under the  License is distributed on an "AS IS" BASIS,
 * WITHOUT  WARRANTIES OR CONDITIONS  OF ANY KIND, either  express  or
 * implied.
 *
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.mockfu;

import static org.mockfu.Mocks.*;

import org.testng.annotations.Test;

public class UnitBuilderTest
{

    @Test
    public void getFields() throws Exception
    {
        UnitBuilder<Unit> unitBuilder = UnitBuilder.of(Unit.class);
        Unit unit = unitBuilder.createUnit();
        assertThat(unit.getClass() != Unit.class);

        unit.checkInstanceFields();
    }

    @Test
    public void setFields()
    {
        UnitBuilder<Unit> unitBuilder = UnitBuilder.of(Unit.class);
        Unit unit = unitBuilder.createUnit();
        assertThat(unit.getClass() != Unit.class);

        unit.setFields();
    }

    @Test
    public void getStaticFields()
    {
        UnitBuilder<Unit> unitBuilder = UnitBuilder.of(Unit.class);
        Unit unit = unitBuilder.createUnit();
        assertThat(unit.getClass() != Unit.class);

        assertThat(unit.getStaticFields(), 3);
    }

    @Test
    public void setStaticFields()
    {
        UnitBuilder<Unit> unitBuilder = UnitBuilder.of(Unit.class);
        Unit unit = unitBuilder.createUnit();
        assertThat(unit.getClass() != Unit.class);

        unit.setStaticFields();
    }

    @Test
    public void unitAccess()
    {
        UnitBuilder<Unit> unitBuilder = UnitBuilder.of(Unit.class);
        Unit unit = unitBuilder.createUnit();
        assertThat(unit.getClass() != Unit.class);

        boolean assertionsEnabled = false;
        assert assertionsEnabled = true;
        assertThat(assertionsEnabled);

        unit.checkUnitAccess();
    }

    @Test
    public void innerClassAccess()
    throws Throwable
    {
        UnitBuilder<Unit> unitBuilder = UnitBuilder.of(Unit.class);
        Unit unit = unitBuilder.createUnit();
        assertThat(unit.getClass() != Unit.class);

        boolean assertionsEnabled = false;
        assert assertionsEnabled = true;
        assertThat(assertionsEnabled);

        unit.checkInnerClassAccess();
    }



    public static class Unit
    {

        public static class Base extends RuntimeException
        {
            private static final long serialVersionUID = 6874539628461108816L;
            /* this field must be referenced with a different owning class */
            protected int field = 1;

            private Base(String name)
            {
            }

            protected int getField()
            {
                return field;
            }
        }

        /* used to test reflective operations on private inner classes */
        private static class InnerClass extends Base
        {
            private static final long serialVersionUID = 5480137351454938543L;

            public String name;
            private int id = 1;

            public InnerClass(String name)
            {
                super(name);
                this.name = name;
            }

            public InnerClass()
            {
                super("");
            }

            public InnerClass(InnerClass other)
            {
                super(other.name);
                this.name = other.name;
            }

            public void setName(String name)
            {
                this.name = name;
                this.id = 1;
            }

            public InnerClass getSelf()
            {
                return this;
            }

            public static int getId()
            {
                return 1;
            }


            public int get(int foo)
            {
                return foo;
            }

            public int get(int[] foo)
            {
                return foo[0];
            }
        }

        private static final String CONSTANT = new String();

        protected static int si;
        private static long sl;

        private String o = "";
        int i;
        private short s;
        private byte b;
        private boolean z;
        private char c;
        private float f;
        private long l;
        private double d;

        public Unit()
        {
        }

        public Unit(Unit other)
        {
        }

        public Unit(InnerClass ic)
        {
        }

        public void checkInstanceFields()
        {
            assert (new String(o) != null);
            assert (Integer.valueOf(i) != null);
            assert (Short.valueOf(s) != null);
            assert (Byte.valueOf(b) != null);
            assert (Boolean.valueOf(z) != null);
            assert (Character.valueOf(c) != null);
            assert (Float.valueOf(f) != null);
            assert (Long.valueOf(l) != null);
            assert (Double.valueOf(d) != null);
        }

        public void setFields()
        {
            o = "hello";
            i = 9;
            s = 9;
            b = 9;
            z = true;
            c = 'A';
            f = 9.0f;
            l = 9L;
            d = 9.0;
        }

        public int getStaticFields()
        {
            int count = 0;
            count += Integer.valueOf(si) != null ? 1 : 0;
            count += Long.valueOf(sl) != null ? 1 : 0;
            count += CONSTANT != null ? 1 : 0;
            return count;
        }



        public void setStaticFields()
        {
            si = 9;
            sl = 9L;
        }

        public void checkUnitAccess()
        {
            /* new instances of self should be subclass, not original class */
            assert new Unit().getClass() != Unit.class;
            assert new Unit(this).getClass() != Unit.class;
            /* private/final methods should call subclass, not original class */
            assert checkUnitAccess1("", new byte[0]);
            assert checkUnitAccess2();
            checkUnitAccess3(new String[] {});
            assert checkUnitAccess4();
            assert checkUnitAccess5(this).checkUnitAccess2();
        }

        private boolean checkUnitAccess1(String s, byte[] b)
        {
            return this.getClass() != Unit.class;
        }

        public final boolean checkUnitAccess2()
        {
            return this.getClass() != Unit.class;
        }

        private static long[][] checkUnitAccess3(String[] s)
        {
            return null;
        }

        private final boolean checkUnitAccess4()
        {
            return this.getClass() != Unit.class;
        }

        private Unit checkUnitAccess5(Unit unit)
        {
            return unit;
        }

        public int checkInnerClassAccess()
        throws Throwable
        {
            int count = 0;

            InnerClass ic = new InnerClass("foo");
            ic.setName("bar");

            assert ic.getCause() == null;
            assert ic.name.equals("bar");
            assert ic.id == 1;
            assert ic.field == 1;
            assert ic.getField() == 1;
            assert InnerClass.getId() == 1;
            assert ic.getClass() == InnerClass.class;
            assert ic.getSelf() != null;
            assert ic.get(new int[] {1}) == 1;
            assert ic.get(1) == 1;
            assert new InnerClass(ic).name.equals("bar");
            assert getInnerClassValues0(ic, this).id == 1;
            assert new Unit(ic).getClass() != Unit.class;

            Object o = ic;
            assert o instanceof InnerClass;
            assert !(o instanceof Unit);
            assert ((InnerClass)o).id == 1;

            try
            {
                o = this;
                ((InnerClass)o).setName("waldo");
            }
            catch (Exception ex)
            {
//                count++;
                return count;
            }
//
//            try
//            {
//                throw (Throwable)o;
//            }
//            catch (InnerClass ex)
//            {
//                count++;
//            }
//            catch (RuntimeException ex)
//            {
//                count = -1;
//            }


            return count;
        }


        /* method parameters and return value should be rewritten to reference subclass */
        final InnerClass getInnerClassValues0(InnerClass ic, Unit unit)
        {
            ic.setName("qaz");
            if (this.getClass() == Unit.class)
                return null;
            return ic;
        }
    }

}
